# encoding: utf-8

from time import time, strftime, strptime
import datetime
import re
import cPickle
import string
from random import Random
from django.contrib import auth

def new_unix_serial():
	"Returns a unix(R) timestamp as the new SOA record serial number"
	return int(time())
	
def new_rfc_serial():
	"Returns the current date plus integer as new SOA record serial in RFC compliant form"
	return strftime("%Y%m%d01")
	
def up_unix_serial():
	"""
	Returns a new unix(R) timestamp serial by returning new_unix_serial().
	Assumes one update per second is enough.
	Wraps somewhen in 2038, oh well. ;)
	"""
	return new_unix_serial()
		
def up_rfc_serial(string):
	"""
	Returns an incremented RFC compliant serial if the update happened on the same day.
	Returns a new new_rfc_serial() if its a shiny new day.
	Timleaps us to tomorrow if there were more than 99 updates for today.
	"""
	if re.match(r"^(\d{4})(\d{2})(\d{2})(\d{1,2})", string):
		match=re.match(r"^(\d{4})(\d{2})(\d{2})(\d{1,2})", string)
		y=match.group(1)
		m=match.group(2)
		d=match.group(3)
		p=match.group(4)
		year=int(y)
		month=int(m)
		day=int(d)
		i=int(p)+1
		s=str(i)
		if len(s) < 2:
			s="0"+s
		if i == 100:
			day=day+1
			d=str(day)
			s="01"
		today=datetime.date.today()
		if today.year <= year and today.month <= month and today.day <= day:
			return y+m+d+s
	return strftime("%Y%m%d01")
	
def get_user_email(request):
	"Returns the logged in users email address from profile or DEFAULT_HOSTMASTER_MAILBOX"		
	if request.user is not None and request.user.is_active:
		return request.user.email
	else:
		return settings.DEFAULT_HOSTMASTER_MAILBOX
		
class UndoOperationDoesNotExist(KeyError):
	"Used to indicate that the requested undo operation with the specific id does not exist"

def user_save_undo(request, model_instance, redirect_to=None):
	"""
	Saves an undo operation to the users session?
	Returns the id of the undo operation.
	If the optional redirect_to argument is not applied, the model_instance must implement
	a get_absolute_url() method.
	
	This function saves the model_instance and all its related objects.
	"""
	# create a list of the model instance and all objects thate relate to the model instance
	seen_objs={}
	model_instance._collect_sub_objects(seen_objs)
	ordered_classes=seen_objs.keys()
	ordered_classes.reverse()
	objs=[]
	for cls in ordered_classes:
		seen_objs[cls]=seen_objs[cls].items()
		for pk,instance in seen_objs[cls]:
			objs.append(instance)
	# serialize the list of model instances
	pickle=cPickle.dumps(objs)
	id=u''.join( Random().sample(string.letters+string.digits, 12) )
	if redirect_to:
		redirto=redirect_to
	else:
		redirto=model_instance.get_absolute_url()
	request.session['undo']={
		'pickle': pickle,
		'id': id,
		'redirect_to': redirto,
		}

def global_save_undo(request, model_instance, redirect_to=None):
	"""
	Saves an undo operation to the undo table.
	Returns the id of the undo operation.
	If the optional redirect_to argument is not applied, the model_instance must implement
	a get_absolute_url() method.

	This function saves the model_instance and all its related objects.
	TODO: adapt
	"""
	# create a list of the model instance and all objects thate relate to the model instance
	seen_objs={}
	model_instance._collect_sub_objects(seen_objs)
	ordered_classes=seen_objs.keys()
	ordered_classes.reverse()
	objs=[]
	for cls in ordered_classes:
		seen_objs[cls]=seen_objs[cls].items()
		for pk,instance in seen_objs[cls]:
			objs.append(instance)
	# serialize the list of model instances
	pickle=cPickle.dumps(objs)
	id=u''.join( Random().sample(string.letters+string.digits, 12) )
	if redirect_to:
		redirto=redirect_to
	else:
		redirto=model_instance.get_absolute_url()
	request.session['undo']={
		'pickle': pickle,
		'id': id,
		'redirect_to': redirto,
		}
		
def user_do_undo(request, undo_id):
	"""
	Executes an undo operation local to the user.
	Returns the URL to redirect to, after the undo operation has executed.
	Raises UndoOperationDoesNotExist if undo_id does not match the currently saved operation.
	
	It is recommended to use the supplied generic undo view in dns.views instead of this function.
	Please note that undo covers all active applications but the code has to live somewhere.
	"""
	try:
		undo=request.session['undo']
		del request.session['undo']
		# check if the undo operation id matches the requested id
		if undo['id'] != undo_id:
			raise KeyError
	except KeyError:
		raise UndoOperationDoesNotExist, u'The undo operation does not exist'
	objects=cPickle.loads(undo['pickle'])
	for obj in objects:
		obj.save()
	return undo['redirect_to']
	

def global_do_undo(request, undo_id):
	"""
	Executes a global undo operation.
	Returns the URL to redirect to, after the undo operation has executed.
	Raises UndoOperationDoesNotExist if undo_id does not match the currently saved operation.

	It is recommended to use the supplied generic undo view in dns.views instead of this function.
	Please note that undo covers all active applications but the code has to live somewhere.
	"""
	try:
		undo=request.session['undo']
		del request.session['undo']
		# check if the undo operation id matches the requested id
		if undo['id'] != undo_id:
			raise KeyError
	except KeyError:
		raise UndoOperationDoesNotExist, u'The undo operation does not exist'
	objects=cPickle.loads(undo['pickle'])
	for obj in objects:
		obj.save()
	return undo['redirect_to']
